#!/usr/bin/env python
"""
Simple and useful library for creating SVG files.
Writen in pure python it's can be used in any product and designed
especialy for web applications.
"""
_wroot='''<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
                     "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
'''

class CDATA:
    '''Simple CDATA container.
    Be careful, it's doesn't quote internal data with <[CDATA and others...'''
    def __init__(self, text):
        self.text = text
    
    def __str__(self):
        return self.text

class Element:
    '''XML element.'''
    def __init__(self, name, attrs={}):
        self.attrs = attrs
        self.name  = name
        self.xml   = []
        
    def __str__(self):
        out = "<" + self.name
        out += ' ' + ' '.join(["%s='%s'" % (x, self.attrs[x]) for x in self.attrs.keys()])
        if len(self.xml) == 0:
            out += '/>'
        else:
            out += '>' + ''.join([str(x) for x in self.xml]) + '</' + self.name + '>'
        return out
    
    def addElement(self, element):
        '''Add subelement in this element tag.'''
        self.xml.append(element)
        return element
    
    def addCDATA(self, cdata):
        '''Add CDATA.
        First parameter can be string or CDATA element.'''
        if type(cdata) == str:
            self.xml.append(CDATA(cdata))
        else:
            self.xml.append(cdata)

class Path:
    '''SVG path class.
    Please note that this class use tuple (x,y), not separate x and y value.
    So typical usage is:
    path = Path((0,50))
    path.splines()
    path.dot((50, 80))
    path.dot((90,100))
    path.dot((80,100))
    print path
    '''
    def __init__(self, startingspoint=None):
        self.path = []
        self.lineType = 'L'
        self.NI = True
        if startingspoint != None and \
           type(startingspoint) == tuple and \
           len(startingspoint) == 2:
            self.moveto(startingspoint)
            
    def moveto(self, coord):
        '''Move pointer to new coords.'''
        self.path.append('M')
        self.path.append(coord)
        self.NI = True
        
    def dot(self, coord):
        '''Add point to current operation.'''
        if self.NI:
            self.path.append(self.lineType)
        self.path.append(coord)
        self.NI = False

    def lines(self, abs=True):
        '''Switch to mode for lines.
        Set abs to False for relative coords.'''
        self.lineType = abs and 'L' or 'l'
        self.NI = True
        
    def splines(self, abs=True):
        '''Switch to mode for splines.
        Set abs to False for relative coords.'''
        self.lineType = abs and 'S' or 's'
        self.NI = True

    def tolines(self):
        '''Convert all splines to lines.'''
        self._convertto({ 'S': 'L', 'L': 'L', 's': 'l', 'l': 'l', 'M': 'M', 'm': 'm'})

    def tosplines(self):
        '''Convert all lines to splines.'''
        self._convertto({ 'S': 'S', 'L': 'S', 's': 's', 'l': 's', 'M': 'M', 'm': 'm'})

    def _convertto(self, mapping):
        '''Convert all mimical values to something else by mapping.'''
        self.path = [ (type(val) == tuple and val)
                        or mapping[val]
                      for val in self.path]
        return

    def __str__(self):
        return ' '.join([ 
                            type(val) == str and val or '%f,%f' % val 
                        for val in self.path])

class SVG:
    '''SVG creator class.
    I try to drop out all unnesessary parameters to styles. So typical usage is:
    svg = SVG()
    svg.rect  (20,  20,  250, 100, style="fill:red;  stroke:black; stroke-width:5; opacity:0.5")
    svg.circle(100, 100, 25,       style="fill:blue; stroke:black; stroke-width:5; opacity:0.5")
    svg.line  (21,  15,  230, 70,  style="stroke: green; stroke-width:7")
    '''
    def __init__(self, width='100%', height='100%'):
        self.preRoot = _wroot
        self.xml = Element('svg', {
            'height' : height,
            'width'  : width,
            'version': "1.1",
            'xmlns'  : "http://www.w3.org/2000/svg",
        })

    def rect(self, x, y, width, height, rx=None, ry=None, style=None):
        '''Draw a rectangle from x,y to width,height.
        Rx, Ry is a 'roundness' parameters, use it carefully.
        And use style for define view of this figure'''
        attrs = { 'x': x, 'y': y, 'width': width, 'height': height }
        if rx:    attrs['rx']    = rx
        if ry:    attrs['ry']    = ry
        if style: attrs['style'] = style
        return self.xml.addElement(Element('rect', attrs))
      
    def circle(self, x, y, radius, style=None):
        '''Draw a circle from x,y with radius r.
        Use style for define view of this figure'''        
        attrs = { 'cx': x, 'cy': y, 'r': radius }
        if style: attrs['style'] = style
        return self.xml.addElement(Element('circle', attrs))

    def line(self, x1, y1, x2, y2, style=None):
        '''Draw line from x1,y2 to x2,y3.
        Use style for define view of this figure'''        
        attrs = { 'x1': x1, 'y1': y1, 'x2': x2, 'y2': y2 }
        if style: attrs['style'] = style
        return self.xml.addElement(Element('line', attrs))

    def path(self, pathv, style=None):
        '''Draw path from pathv
        Use style for define view of this figure'''
        attrs = { 'd': str(pathv) }
        if style: attrs['style'] = style
        return self.xml.addElement(Element('path', attrs))
    
    def __str__(self):
        return self.preRoot + str(self.xml)

if __name__ == '__main__':
    svg = SVG()
    svg.rect  (20,  20,  250, 100, style="fill:red;  stroke:black; stroke-width:5; opacity:0.5")
    svg.circle(100, 100, 25,       style="fill:blue; stroke:black; stroke-width:5; opacity:0.5")
    svg.line  (21,  15,  230, 70,  style="stroke: green; stroke-width:7")
    path = Path((0,100))
    path.lines()
    [path.dot(x) for x in [
        (0,100), (50, 130), (100,150), (150,100), (200,70), (250,65), (300,60),(300,60)]]
    svg.path(path, style="fill:none;stroke:black; stroke-width:2")
    path.tosplines()
    svg.path(path, style="fill:none;stroke:blue; stroke-width:2")
    print svg
